package com.hqd.ch03.v50.boot.web.servlet;

import com.hqd.ch03.utils.ClassUtils;
import com.hqd.ch03.v50.boot.autoconfigure.web.servlet.RegistrationBean;

import javax.servlet.*;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionIdListener;
import javax.servlet.http.HttpSessionListener;
import java.util.Collections;
import java.util.EventListener;
import java.util.HashSet;
import java.util.Set;

public class ServletListenerRegistrationBean<T extends EventListener> extends RegistrationBean {

    private static final Set<Class<?>> SUPPORTED_TYPES;

    static {
        Set<Class<?>> types = new HashSet<>();
        types.add(ServletContextAttributeListener.class);
        types.add(ServletRequestListener.class);
        types.add(ServletRequestAttributeListener.class);
        types.add(HttpSessionAttributeListener.class);
        types.add(HttpSessionIdListener.class);
        types.add(HttpSessionListener.class);
        types.add(ServletContextListener.class);
        SUPPORTED_TYPES = Collections.unmodifiableSet(types);
    }

    private T listener;

    /**
     * Create a new {@link ServletListenerRegistrationBean} instance.
     */
    public ServletListenerRegistrationBean() {
    }

    /**
     * Create a new {@link ServletListenerRegistrationBean} instance.
     *
     * @param listener the listener to register
     */
    public ServletListenerRegistrationBean(T listener) {
        this.listener = listener;
    }

    /**
     * Returns {@code true} if the specified listener is one of the supported types.
     *
     * @param listener the listener to test
     * @return if the listener is of a supported type
     */
    public static boolean isSupportedType(EventListener listener) {
        for (Class<?> type : SUPPORTED_TYPES) {
            if (ClassUtils.isAssignableValue(type, listener)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Return the supported types for this registration.
     *
     * @return the supported types
     */
    public static Set<Class<?>> getSupportedTypes() {
        return SUPPORTED_TYPES;
    }

    /**
     * Return the listener to be registered.
     *
     * @return the listener to be registered
     */
    public T getListener() {
        return this.listener;
    }

    /**
     * Set the listener that will be registered.
     *
     * @param listener the listener to register
     */
    public void setListener(T listener) {
        this.listener = listener;
    }

    @Override
    protected String getDescription() {
        return "listener " + this.listener;
    }

    @Override
    protected void register(String description, ServletContext servletContext) {
        try {
            servletContext.addListener(this.listener);
        } catch (RuntimeException ex) {
            throw new IllegalStateException("Failed to add listener '" + this.listener + "' to servlet context", ex);
        }
    }

}
